﻿/* 
* Copyright (c) 2009, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
using Bio.Data.Interfaces;
using System.Xml.Linq;
using LabelRNA;

namespace Bio.Data.Providers.Structure
{
    /// <summary>
    /// This loads the bpseq file format
    /// </summary>
    class BPSeqFile : BioFileDataLoader<IStructureModelBioEntity>
    {
        private const string BPDefinition = @"\d\s[a-zA-Z]\s\d";
        private Dictionary<int, int> _basePairs;
        private SimpleRNASequence _sequence;

        /// <summary>
        /// Here we read the BPSeq file and detect any errors, this is our opportunity to fail out if there are errors.
        /// </summary>
        /// <returns></returns>
        protected override bool OnInitialize()
        {
            if (base.OnInitialize())
            {
                _basePairs = new Dictionary<int, int>();
                _sequence = new SimpleRNASequence() { Id = string.Empty, SourceId = string.Empty, ScientificName = string.Empty };
                using (var reader = File.OpenText(InitializationData))
                {
                    string line;
                    while (!string.IsNullOrEmpty(line = reader.ReadLine()))
                    {
                        if (Regex.IsMatch(line, BPDefinition))
                        {
                            string[] tokens = Regex.Split(line, @" ");
                            int idx1 = Int32.Parse(tokens[0])-1;
                            int idx2 = Int32.Parse(tokens[2])-1;

                            if (idx1 == _sequence.SequenceData.Count)
                            {
                                _sequence.AddSymbol(tokens[1][0]);
                            }
                            else { return false; } //Error, nucleotides are expected in ascending order.

                            if (idx1 == idx2) { return false; } //Error, a nucleotide can't be paired to itself
                            if ((idx1 < -1) || (idx2 < -1)) { return false; } //Error, an invalid index is specified

                            if (idx2 > -1)
                            {
                                if (idx1 < idx2)
                                {
                                    //We pickup the base pair
                                    if (!_basePairs.ContainsKey(idx1) && !_basePairs.ContainsValue(idx2))
                                    {
                                        _basePairs.Add(idx1, idx2);
                                    }
                                    else { return false; } //Consistency Error, a position is involved in two base pairs
                                }
                                else //We should have this base pair and are looking at the inverse, confirm
                                {
                                    if (!(_basePairs.ContainsKey(idx2) && _basePairs[idx2] == idx1)) { return false; } //Consistency Error.
                                }
                            }
                            else 
                            { 
                                //Check to make sure this position wasn't already paired.
                                if (_basePairs.Values.Contains(idx1)) { return false; } //Consistency Error, this position was paired but thinks its not.
                            }
                        }
                    }

                    if (_basePairs.Values.Count((idx) => (idx >= _sequence.SequenceData.Count)) > 0) { return false; } //One of the three prime indexes is outside the bounds of the sequence
                }
                return true;
            }
            return false;
        }

        protected override void OnLoad()
        {
            var basePairs = _basePairs.Select(bp => new RNASecondaryStructureBasePair(_sequence, bp.Key, bp.Value));
            try
            {
                RNAParser labelRNAParser = new RNAParser(_basePairs, _sequence.SequenceData.Count);
                XElement extentsXML = labelRNAParser.ParseToExtents();
                var extents = from extent in extentsXML.Descendants("Extent")
                                select new RNASecondaryStructureExtent(_sequence,
                                                                     Int32.Parse(extent.Element("ExtentID").Value),
                                                                     Int32.Parse(extent.Element("ExtentOrdinal").Value),
                                                                     (StructureExtentType)Enum.Parse(typeof(StructureExtentType), extent.Element("ExtentTypeID").Value, true),
                                                                     Int32.Parse(extent.Element("ExtentStartIndex").Value),
                                                                     Int32.Parse(extent.Element("ExtentEndIndex").Value));
                Entities.Add(new RNASecondaryStructureModel(InitializationData, _sequence)
                {
                    BasePairs = basePairs.Cast<IBasePairBioEntity>().ToList(),
                    Extents = extents.Cast<IStructureExtentBioEntity>().ToList()
                });
            }
            catch (Exception) //We ran into some error parsing the extents.
            {
                Entities.Add(new RNASecondaryStructureModel(InitializationData, _sequence) 
                { 
                    BasePairs = basePairs.Cast<IBasePairBioEntity>().ToList()
                });
            }
        }
    }
}
